/**
 *			\author ITII - ISEN - P6
 *			\author Benjamin Mur
 *			\file SmtpServer.cpp
 *			\version 0.2
 *			\date 10/09/2010
 */


#include <iostream>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include "SmtpServer.h"
#include "SmtpProtocol.h"


using namespace std;


SmtpServer::SmtpServer(int port)
{
	cout << "Starting The SmtpServer..." << endl;
	_bufferSize = 1024;
	_init = false;
	_fillOut = false;


	_tcpServ = new TcpServer(port);
	_buffer = new char[_bufferSize];

	if( true == _tcpServ->isInit() )
		_init = true;
}

SmtpServer::~SmtpServer()
{
	delete _buffer;
	delete _tcpServ;
}

Mail SmtpServer::receiveMail()
{
	int ret = 1;
	int step = 0;
	string sendBuffer;
	_fillOut = false;

	sendBuffer = "Bienvenue sur ITII - ISEN - P6, SMTP-Server v0.2\r\n";
	if( -1 == _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) ) )
	{
		#ifdef _DEBUF
		cerr << "Error: serverSend()" << endl;
		#endif
		ret = -1;
	}

	while( ret==1 )
	{
		receiveCommand(_buffer); /* Recois les Données */
		ret = process( _buffer, step );      /* Traite les Données */
		cout << "Buffer received: " << _buffer << endl;
	}

	if( -1 == ret )
	{
		cerr << "Error while receiving a mail!" << endl;
	}

	if( 0 == ret )
		_fillOut = true;

	return _mail;
}

bool SmtpServer::isFillOut() const
{
	return _fillOut;
}

int SmtpServer::receiveCommand(char * buffer)
{
    char	buf;
    char *	bufferTmp;
    int		i		= 0;
    int		err		= 0;

    memset( buffer, '\0', _bufferSize );       /* Vide le Buffer */
    buf = '\0';
    while ( buf != 0x0A )             /* Fin lorsque Entrer est détecté */
    {
        /* Recoie les Données */
        err = _tcpServ->serverReceive( &buf, 1 );
        if (err < 0)
        {
            /* Si il y a une erreur a la réception des données, quitte */
            cout << "Error while receiving a command" << endl;
            return -1;
        }
        if (buf != '\n' && buf != '\r')
        {
            //Si il n'y a pas assez de memoire dans _buffer
            //On réalloue le _buffer
            if( _bufferSize <= i )
            {
            	bufferTmp = new char[_bufferSize];

            	strcpy( bufferTmp, buffer );

            	delete _buffer;
            	_bufferSize += _bufferSize;
            	_buffer = new char[_bufferSize];

            	buffer = _buffer;
            	strcpy( buffer, bufferTmp );

            	delete bufferTmp;
            }
            buffer[i] = buf;
            i++;
        }
        cout << buf;                /* Affiche les touches saisies a l'écran */
    }
    return 0;
}

char * SmtpServer::extractCommand( char * texte, int car, int occurence )
{
    char    *ptr,               // Pointe vers le premier car. trouvé
            *start;             // Début de la recherche dans le texte
    int     i = 0,              // Occurence actuel
            nb;                 // Nombre de caractere a copier

    start = texte;

    for (i=0; i<occurence; i++)
    {
        ptr = strchr (start, car);

        if (ptr == NULL && occurence == 1)
        {
            /* cas ou on demande la 1ere occurence et que le texte n'est pas séparé */
            ptr = (char*) malloc (strlen(texte)+1);
            strcpy(ptr, texte);
            return ptr;         // Renvoi : texte
        }
        else if (occurence-1 == i)
        {
            if (ptr == NULL)
                ptr = texte + strlen(texte);

            nb = ptr - start;
            ptr = (char*) malloc (nb+1);
            for (int j=0; j<nb; j++)
                ptr[j] = texte[start-texte+j];
            ptr[nb] = '\0';
            return ptr;
        }
        else if (ptr == NULL)
            /* pas d'occurence trouvée */
            return NULL;        // Renvoi : NULL
        else
            start = ptr+1;
    }
    return NULL;
}

bool SmtpServer::isInit() const
{
	return _init;
}

int SmtpServer::process( char * buffer, int & step )
{
    string		sendBuffer;
    char *		ptr = NULL;
    ostringstream	out;
    char    Commands [7][6] = { "EHLO\0",   // connection
                                "HELO\0",
                                "MAIL\0",   // Adresse de l'envoyeur
                                "RCPT\0",   // Adresse du receveur
                                "DATA\0",   // Données
                                ".\0",      // <CRLF>.<CRLF> fin des données
                                "QUIT\0" }; // Déconnection

	ptr = extractCommand (buffer, ' ', 1);
	if (ptr == NULL)
	{
		sendBuffer = "502 Error: command not implemented\r\n";
		_tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
		cerr << sendBuffer << endl;
		return -1;
	}

    if (!strcmp(ptr, Commands[0]) || !strcmp(ptr, Commands[1])) // HELO OR EHLO
    {
        free (ptr);
        if (step == 4)
        {
        }
        else if (step != 0)
        {
            sendBuffer = "503 Error: nested command\r\n";
            _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
            cerr << sendBuffer << endl;
            return -1;
        }
        else if ( (ptr = extractCommand (buffer, ' ', 2)) == NULL )
        {
            sendBuffer = "500 Error: bad syntax\r\n501 Syntax: HELO SMTP-Server v0.2\r\n";
            _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
            cerr << sendBuffer << endl;
            return -1;
        }
        else
        {
		//Convert int to string
		out << _bufferSize;

		sendBuffer = "250-SMTP-Server v0.2\r\n250-PIPELINING\r\n250-SIZE ";
		sendBuffer += out.str();
		sendBuffer += "\r\n250 8BITMIME\r\n";
            _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
            step++;     /* Etape suivante step == 1 */
            free (ptr);
            cout << sendBuffer << endl;
            return 1;
        }
    }
    else if (!strcmp(ptr, Commands[2]) || !strcmp(ptr, Commands[3]))  // MAIL OR RCPT
    {
        free (ptr);
        ptr = extractCommand (buffer, ':', 1);
        if (ptr == NULL)
        {
            sendBuffer = "502 Error: command not implemented\r\n";
            _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
            cerr << sendBuffer << endl;
            return -1;
        }
        else if (!strcmp(ptr, "MAIL FROM"))
        {
            free (ptr);

            switch (step)
            {
                case 0 :
                    sendBuffer = "503 Error: send HELO/EHLO first\r\n";
                    _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                    cerr << sendBuffer << endl;
                    return -1;
                    break;
                case 2 :
                case 3 :
                    sendBuffer = "503 Error: nested command\r\n";
                     _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                    cerr << sendBuffer << endl;
                     return -1;
                    break;
                case 4 :
                    break;
                default :
                    sendBuffer = "250 Sender Ok\r\n";
                     _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                    step++; /* Etape suivante step == 2 */
                    cout << sendBuffer << endl;
			_mail.setMailFrom(extractMailAddress(buffer));
                    return 1;
            }
        }
        else if (!strcmp(ptr, "RCPT TO"))
        {
            free (ptr);

            switch (step)
            {
                case 0 :
                    sendBuffer = "503 Error: send HELO/EHLO first\r\n";
                    _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                    cerr << sendBuffer << endl;
                    return -1;
                    break;
                case 1 :
                    sendBuffer = "503 Error: send MAIL FROM first\r\n";
			_tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                    cerr << sendBuffer << endl;
                    return -1;
                    break;
                case 3 :
                    sendBuffer = "503 Error: nested command\r\n";
                    _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                    cerr << sendBuffer << endl;
                    return -1;
                    break;
                case 4 :
                    break;
                default :
                    sendBuffer = "250 Recipient Ok\r\n";
                    _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                    step++; /* Etape suivante step == 3 */
                    cout << sendBuffer << endl;
                    _mail.setMailTo(extractMailAddress(buffer));
                    return 1;
            }
        }
        else
        {
            free (ptr);
            sendBuffer = "502 Error: command not implemented\r\n";
            _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
            cerr << sendBuffer << endl;
            return -1;
        }
    }
    else if (!strcmp(ptr, Commands[4]))     // DATA
    {
        free (ptr);

        switch (step)
        {
            case 0 :
                sendBuffer = "503 Error: send HELO/EHLO first\r\n";
                _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                cerr << sendBuffer << endl;
                return -1;
                break;
            case 1 :
                sendBuffer = "503 Error: send MAIL FROM first\r\n";
                _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                cerr << sendBuffer << endl;
                return -1;
                break;
            case 2 :
                sendBuffer = "503 Error: send RCPT TO first\r\n";
                _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                cerr << sendBuffer << endl;
                return -1;
                break;
            case 4 :
                break;
            default :
                sendBuffer = "354 End data with <CR><LF>.<CR><LF>\r\n";
                _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
                step++; /* Etape suivante step == 4 */
                cout << sendBuffer << endl;
                return 1;
        }
    }
    else if (!strcmp(ptr, Commands[5]))     // .
    {
        if (step == 4)
        {
            sendBuffer = "250 Ok\r\n";
            _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
            step++;     /* Etape suivante step == 5 */
            cout << sendBuffer << endl;
            _mail.setBody(_data);
            return 1;
        }
    }
    else if (!strcmp(ptr, Commands[6]))     // QUIT
    {
        if (step != 4) {
            free (ptr);
            sendBuffer = "221 Closing connection\r\n";
            _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
            cout << sendBuffer << endl;
		return 0;
        }
    }
    else
    {
        if (step != 4) {
            free (ptr);
            sendBuffer = "502 Error: command not implemented\r\n";
            _tcpServ->serverSend( (char *)sendBuffer.c_str(), strlen(sendBuffer.c_str()) );
            cerr << sendBuffer << endl;
            return -1;
        }
    }

    _data += buffer;

    return 1;
}

string SmtpServer::extractMailAddress(const char * buffer)
{
	char *	ptr;
	string	mailAddress;

	ptr = strchr( (char *)buffer, '<' );
	if( NULL == ptr )
	{
		ptr = strchr( (char *)buffer, ':' );
		if( NULL == ptr )
		{
			mailAddress = buffer;
			return mailAddress;
		}
		mailAddress = ptr;
		return mailAddress;
	}

	ptr += 1;

	while( *ptr != '>' && *ptr != '\0' )
	{
		if( '>' != *ptr )
			mailAddress += *ptr;

		ptr += 1;
	}
	return mailAddress;
}
